package com.ybliang.plat.common.rpc.consumer.client;

import com.ybliang.plat.common.rpc.consumer.client.annotation.RpcReference;
import com.ybliang.plat.common.rpc.consumer.config.RpcClientProperties;
import com.ybliang.plat.common.rpc.consumer.proxy.RpcClientProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liangyb1
 */
@Configuration
@EnableConfigurationProperties(RpcClientProperties.class)
public class RpcReferenceBeanPostProcessor implements BeanPostProcessor {

    public static Logger LOGGER = LoggerFactory.getLogger(RpcReferenceBeanPostProcessor.class);

    private final Map<String, Object> rpcClientMap = new ConcurrentHashMap<>();

    @Autowired
    RpcClientProperties rpcClientProperties;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if(field.isAnnotationPresent(RpcReference.class)) {
                if(!field.getType().isInterface()) {
                    throw new BeanCreationException("RpcReference field must be declared an interface");
                }else {
                    // field赋值
                    setValue(field, bean);
                }
            }
        }
        return bean;
    }

    private void setValue(Field field, Object bean) {
        //设置该域可设置修改
        field.setAccessible(true);
        try {
            Class<?> inf = field.getType();
            Object proxyBean = rpcClientMap.get(inf.getName());
            LOGGER.debug("RpcReferenceBeanPostProcessor bean:[{}], map get bean key:{}, value:{}", bean, inf.getName(), proxyBean);
            if(null == proxyBean) {
                RpcClientProxy proxy = new RpcClientProxy(inf, rpcClientProperties);
                proxyBean = Proxy.newProxyInstance(inf.getClassLoader(), new Class[]{inf}, proxy);
                rpcClientMap.put(inf.getName(), proxyBean);
                LOGGER.debug("RpcReferenceBeanPostProcessor bean:[{}], newProxyInstance:{}", bean, proxyBean);
            }
            field.set(bean, proxyBean);
        } catch (IllegalAccessException e) {
            LOGGER.error(e.getMessage(), e);
        }
    }


}
